﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Routing;
using SimpleRest.Core;
using System.IO;
using SimpleRest.Core.Routing;

namespace SimpleRest.Core
{
    /// <summary>
    /// Utility created to assist with discovery of IHttpApplication objects within assembilies.
    /// </summary>
    public static class RestBootstrapperUtility
    {
        private static List<IRestBootstrapper> RegisteredBootstrappers = new List<IRestBootstrapper>();

        /// <summary>
        /// Iterates through each http application specified and initializes them.  This is useful in applications where there are multiple http application's.
        /// </summary>
        /// <param name="httpapplications"></param>
        public static void InitializeAllRestBootstrappers(bool ignorePreviouslyInitialized = true, bool ignoreAssemblyResolveErrors = true, params IRestBootstrapper[] httpapplications)
        {
            if (httpapplications == null || httpapplications.Count() == 0)
            {
                var items = GetAllRestBootstrappers();
                if (items != null)
                    httpapplications = items.ToArray();
            }

            if (httpapplications == null)
                return;
            foreach (var application in httpapplications)
                if (ignorePreviouslyInitialized || (!ignorePreviouslyInitialized && !RegisteredBootstrappers.Contains(application)))
                    application.PreInitialize();

            Dictionary<System.Reflection.Assembly, Configuration> assembilies = new Dictionary<System.Reflection.Assembly, Configuration>();

            bool isHttpConfigurationFound = false;

            foreach (var application in httpapplications)
            {
                if (!ignorePreviouslyInitialized && RegisteredBootstrappers.Contains(application))
                    continue;

                Configuration configuration = null;
                var args = new ApplicationExecutionEventArgs();
                application.Initialize(application, args);

                foreach (KeyValuePair<Type, Configuration> serviceToBeRegistered in args.ServicesToRegister)
                    ServiceDiscoveryFactory.RegisterService(serviceToBeRegistered.Key, serviceToBeRegistered.Value);

                if (configuration == null)
                    configuration = args.ScanAndRegisterConfiguration;
                if (configuration == null)
                    configuration = new Configuration();

                if (args.Assembilies != null)
                    foreach (var assembly in args.Assembilies)
                        if (!assembilies.ContainsKey(assembly))
                            assembilies.Add(assembly, configuration);
            }

            foreach (var key in assembilies.Keys)
            {
                ServiceDiscoveryFactory.ScanAndRegisterServices(
                configuration: assembilies[key],
                assembilies: key);
            }

            foreach (var application in httpapplications)
                if (ignorePreviouslyInitialized || (!ignorePreviouslyInitialized && !RegisteredBootstrappers.Contains(application)))
                    application.PostInitialize();

            foreach (var application in httpapplications)
                if (!RegisteredBootstrappers.Contains(application))
                    RegisteredBootstrappers.Add(application);
        }
        public static void InitializeAllRestBootstrappers(params IRestBootstrapper[] httpapplications)
        {
            InitializeAllRestBootstrappers(true, true, httpapplications);
        }

        /// <summary>
        /// Returns all http applications within the associated assembilies.
        /// </summary>
        /// <param name="assembilies"></param>
        /// <returns></returns>
        public static List<IRestBootstrapper> GetAllRestBootstrappers(bool ignoreAssemblyResolveErrors = true, params System.Reflection.Assembly[] assembilies)
        {
            if (assembilies == null || assembilies.Length == 0)
                return null;

            List<IRestBootstrapper> items = new List<IRestBootstrapper>();

            foreach (var assembly in assembilies)
            {
                try
                {
                    foreach (var type in assembly.GetTypes())
                    {
                        if (type.GetInterface(typeof(IRestBootstrapper).FullName) != null && !type.IsAbstract)
                        {
                            var obj = Activator.CreateInstance(type) as IRestBootstrapper;

                            items.Add(obj);
                        }
                    }
                }
                catch (Exception ex)
                {
                    if (!ignoreAssemblyResolveErrors)
                        throw new ApplicationException(string.Format("Unable to load assembly types for {0}.", assembly.FullName), ex);
                }
            }

            return items;
        }
        public static List<IRestBootstrapper> GetAllRestBootstrappers(params System.Reflection.Assembly[] assembilies)
        {
            return GetAllRestBootstrappers(true, assembilies);
        }

        /// <summary>
        /// Returns all http applications within all non microsoft assembilies loaded in the AppDomain.
        /// </summary>
        /// <returns></returns>
        public static List<IRestBootstrapper> GetAllRestBootstrappers()
        {
            System.IO.DirectoryInfo dinfo = new System.IO.DirectoryInfo(System.Web.HttpContext.Current.Server.MapPath("~/bin"));
            if (dinfo.Exists)
            {
                List<System.Reflection.Assembly> current = AppDomain.CurrentDomain.GetAssemblies().Where(
                    d => !d.FullName.StartsWith("System") &&
                         !d.FullName.StartsWith("Microsoft") &&
                         !d.FullName.StartsWith("App_global.asax")).ToList();
                foreach (var file in dinfo.GetFiles("*.dll")) // load them all into the app domain.
                {
                    if (!current.Any(d => d.FullName.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)[0] == (file.Name.Replace(".dll", ""))))
                    { 
                        var x = System.Reflection.Assembly.LoadFrom(file.FullName); // loads into app domain.
                        current.Add(x);
                    }
                }

                return GetAllRestBootstrappers(current.ToArray());
            }
            else
                return null;
        }
    }
}
